package com.ruoyi.pos.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.pos.domain.device.PosDevice;
import com.ruoyi.pos.domain.dto.device.request.DeviceHeartbeatRequest;
import com.ruoyi.pos.domain.dto.device.response.DeviceHeartbeatResponse;
import com.ruoyi.pos.service.device.IPosDeviceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 设备心跳WebSocket处理器
 * 用于Android设备实时心跳上报
 * 
 * @author ruoyi
 * @date 2025-01-30
 */
@Component
public class DeviceHeartbeatWebSocketHandler implements WebSocketHandler {

    private static final Logger logger = LoggerFactory.getLogger(DeviceHeartbeatWebSocketHandler.class);

    @Autowired
    private IPosDeviceService posDeviceService;

    // 存储设备WebSocket会话，key为设备编码
    private final Map<String, WebSocketSession> deviceSessions = new ConcurrentHashMap<>();
    
    // 存储设备最后心跳时间
    private final Map<String, Long> lastHeartbeatTime = new ConcurrentHashMap<>();

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        logger.info("设备心跳WebSocket连接建立: sessionId={}", session.getId());
        
        // 从URI中获取设备编码
        String deviceCode = extractDeviceCodeFromUri(session);
        if (deviceCode != null) {
            deviceSessions.put(deviceCode, session);
            lastHeartbeatTime.put(deviceCode, System.currentTimeMillis());
            logger.info("设备 {} 已连接心跳WebSocket", deviceCode);
            
            // 发送欢迎消息
            sendWelcomeMessage(session, deviceCode);
        } else {
            logger.warn("无法从URI中提取设备编码，关闭连接");
            session.close();
        }
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        String payload = message.getPayload().toString();
        logger.debug("收到设备心跳消息: {}", payload);

        try {
            // 解析心跳消息
            Map<String, Object> messageData = objectMapper.readValue(payload, Map.class);
            String messageType = (String) messageData.get("type");
            
            if ("HEARTBEAT".equals(messageType)) {
                handleHeartbeat(session, messageData);
            } else if ("DEVICE_STATUS".equals(messageType)) {
                handleDeviceStatus(session, messageData);
            } else {
                logger.warn("未知的消息类型: {}", messageType);
                sendErrorMessage(session, "未知的消息类型: " + messageType);
            }
        } catch (Exception e) {
            logger.error("处理心跳消息失败", e);
            sendErrorMessage(session, "消息处理失败: " + e.getMessage());
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        logger.error("设备心跳WebSocket传输错误: sessionId={}", session.getId(), exception);
        removeSession(session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        logger.info("设备心跳WebSocket连接关闭: sessionId={}, 状态={}", session.getId(), closeStatus);
        removeSession(session);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 处理设备心跳
     */
    private void handleHeartbeat(WebSocketSession session, Map<String, Object> messageData) {
        try {
            String deviceCode = extractDeviceCodeFromSession(session);
            if (deviceCode == null) {
                sendErrorMessage(session, "设备编码不存在");
                return;
            }
            
            // 构建心跳请求对象
            DeviceHeartbeatRequest request = buildHeartbeatRequest(deviceCode, messageData);
            
            // 更新设备心跳信息到数据库
            updateDeviceHeartbeat(request);
            
            // 更新最后心跳时间
            lastHeartbeatTime.put(deviceCode, System.currentTimeMillis());
            
            // 构建并发送心跳响应
            DeviceHeartbeatResponse response = buildHeartbeatResponse(deviceCode);
            sendHeartbeatResponse(session, response);
            
        } catch (Exception e) {
            logger.error("处理设备心跳失败", e);
            sendErrorMessage(session, "心跳处理失败: " + e.getMessage());
        }
    }

    /**
     * 处理设备状态更新
     */
    private void handleDeviceStatus(WebSocketSession session, Map<String, Object> messageData) {
        try {
            String deviceCode = extractDeviceCodeFromSession(session);
            if (deviceCode == null) {
                sendErrorMessage(session, "设备编码不存在");
                return;
            }
            
            // 获取设备状态
            String status = (String) messageData.get("status");
            Object extraData = messageData.get("data");
            
            logger.info("设备 {} 状态更新: status={}, data={}", deviceCode, status, extraData);
            
            // 更新设备状态到数据库
            PosDevice device = posDeviceService.selectPosDeviceByCode(deviceCode);
            if (device != null) {
                posDeviceService.updatePosDeviceStatus(device.getDeviceId(), status);
            }
            
            // 发送确认消息
            sendSuccessMessage(session, "设备状态已更新");
            
        } catch (Exception e) {
            logger.error("处理设备状态更新失败", e);
            sendErrorMessage(session, "状态更新失败: " + e.getMessage());
        }
    }

    /**
     * 构建心跳请求对象
     */
    private DeviceHeartbeatRequest buildHeartbeatRequest(String deviceCode, Map<String, Object> messageData) {
        DeviceHeartbeatRequest request = new DeviceHeartbeatRequest();
        
        // 查询设备信息获取deviceId
        PosDevice device = posDeviceService.selectPosDeviceByCode(deviceCode);
        if (device != null) {
            request.setDeviceId(device.getDeviceId());
        }
        
        request.setDeviceCode(deviceCode);
        request.setHeartbeatTime(new Date());
        
        // 从消息中提取其他字段
        if (messageData.containsKey("ipAddress")) {
            request.setIpAddress((String) messageData.get("ipAddress"));
        }
        if (messageData.containsKey("macAddress")) {
            request.setMacAddress((String) messageData.get("macAddress"));
        }
        if (messageData.containsKey("deviceStatus")) {
            request.setDeviceStatus((String) messageData.get("deviceStatus"));
        }
        if (messageData.containsKey("appVersion")) {
            request.setAppVersion((String) messageData.get("appVersion"));
        }
        
        return request;
    }

    /**
     * 更新设备心跳信息到数据库
     */
    private void updateDeviceHeartbeat(DeviceHeartbeatRequest request) {
        try {
            if (request.getDeviceId() != null) {
                PosDevice device = new PosDevice();
                device.setDeviceId(request.getDeviceId());
                device.setLastOnlineTime(request.getHeartbeatTime());
                device.setIpAddress(request.getIpAddress());
                device.setStatus("1"); // 1-在线
                device.setUpdateTime(new Date());
                
                posDeviceService.updatePosDevice(device);
                logger.debug("设备 {} 心跳信息已更新", request.getDeviceCode());
            }
        } catch (Exception e) {
            logger.error("更新设备心跳信息失败", e);
        }
    }

    /**
     * 构建心跳响应
     */
    private DeviceHeartbeatResponse buildHeartbeatResponse(String deviceCode) {
        DeviceHeartbeatResponse response = new DeviceHeartbeatResponse();
        response.setDeviceCode(deviceCode);
        response.setSuccess(true);
        response.setMessage("心跳接收成功");
        response.setServerTime(new Date());
        response.setNextHeartbeatInterval(30); // 默认30秒一次心跳
        response.setNeedConfigUpdate(false);
        response.setConfigVersion("1.0");
        
        return response;
    }

    /**
     * 发送心跳响应
     */
    private void sendHeartbeatResponse(WebSocketSession session, DeviceHeartbeatResponse response) {
        try {
            Map<String, Object> message = new ConcurrentHashMap<>();
            message.put("type", "HEARTBEAT_RESPONSE");
            message.put("success", response.getSuccess());
            message.put("message", response.getMessage());
            message.put("serverTime", response.getServerTime().getTime());
            message.put("nextHeartbeatInterval", response.getNextHeartbeatInterval());
            message.put("needConfigUpdate", response.getNeedConfigUpdate());
            message.put("configVersion", response.getConfigVersion());
            
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(message)));
        } catch (Exception e) {
            logger.error("发送心跳响应失败", e);
        }
    }

    /**
     * 发送欢迎消息
     */
    private void sendWelcomeMessage(WebSocketSession session, String deviceCode) {
        try {
            Map<String, Object> message = new ConcurrentHashMap<>();
            message.put("type", "WELCOME");
            message.put("deviceCode", deviceCode);
            message.put("message", "心跳WebSocket连接成功");
            message.put("serverTime", System.currentTimeMillis());
            message.put("heartbeatInterval", 30);
            
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(message)));
        } catch (Exception e) {
            logger.error("发送欢迎消息失败", e);
        }
    }

    /**
     * 发送成功消息
     */
    private void sendSuccessMessage(WebSocketSession session, String message) {
        try {
            Map<String, Object> response = new ConcurrentHashMap<>();
            response.put("type", "SUCCESS");
            response.put("message", message);
            response.put("timestamp", System.currentTimeMillis());
            
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        } catch (Exception e) {
            logger.error("发送成功消息失败", e);
        }
    }

    /**
     * 发送错误消息
     */
    private void sendErrorMessage(WebSocketSession session, String message) {
        try {
            Map<String, Object> response = new ConcurrentHashMap<>();
            response.put("type", "ERROR");
            response.put("message", message);
            response.put("timestamp", System.currentTimeMillis());
            
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        } catch (Exception e) {
            logger.error("发送错误消息失败", e);
        }
    }

    /**
     * 从URI中提取设备编码
     * URI格式: /ws/heartbeat/{deviceCode}
     */
    private String extractDeviceCodeFromUri(WebSocketSession session) {
        try {
            String path = session.getUri().getPath();
            String[] parts = path.split("/");
            // 假设URI格式为 /ws/heartbeat/{deviceCode}
            if (parts.length >= 4 && "heartbeat".equals(parts[2])) {
                return parts[3];
            }
        } catch (Exception e) {
            logger.error("提取设备编码失败", e);
        }
        return null;
    }

    /**
     * 从会话中提取设备编码
     */
    private String extractDeviceCodeFromSession(WebSocketSession session) {
        for (Map.Entry<String, WebSocketSession> entry : deviceSessions.entrySet()) {
            if (entry.getValue().getId().equals(session.getId())) {
                return entry.getKey();
            }
        }
        return null;
    }

    /**
     * 移除会话
     */
    private void removeSession(WebSocketSession session) {
        String deviceCode = extractDeviceCodeFromSession(session);
        if (deviceCode != null) {
            deviceSessions.remove(deviceCode);
            lastHeartbeatTime.remove(deviceCode);
            logger.info("设备 {} 心跳WebSocket会话已移除", deviceCode);
            
            // 更新设备状态为离线
            try {
                PosDevice device = posDeviceService.selectPosDeviceByCode(deviceCode);
                if (device != null) {
                    posDeviceService.updatePosDeviceStatus(device.getDeviceId(), "0"); // 0-离线
                }
            } catch (Exception e) {
                logger.error("更新设备离线状态失败", e);
            }
        }
    }

    /**
     * 向指定设备发送消息
     */
    public void sendToDevice(String deviceCode, Object message) {
        try {
            WebSocketSession session = deviceSessions.get(deviceCode);
            if (session != null && session.isOpen()) {
                String messageJson = objectMapper.writeValueAsString(message);
                session.sendMessage(new TextMessage(messageJson));
            }
        } catch (Exception e) {
            logger.error("发送消息到设备失败: deviceCode={}", deviceCode, e);
        }
    }

    /**
     * 获取在线设备数量
     */
    public int getOnlineDeviceCount() {
        return deviceSessions.size();
    }

    /**
     * 获取所有在线设备编码
     */
    public java.util.Set<String> getOnlineDeviceCodes() {
        return deviceSessions.keySet();
    }

    /**
     * 检查设备是否在线
     */
    public boolean isDeviceOnline(String deviceCode) {
        return deviceSessions.containsKey(deviceCode) && deviceSessions.get(deviceCode).isOpen();
    }

    /**
     * 获取设备最后心跳时间
     */
    public Long getLastHeartbeatTime(String deviceCode) {
        return lastHeartbeatTime.get(deviceCode);
    }
}

